#!/usr/bin/tclsh
#
# +-------------------------------------------------------------------------+
# | e-vigr: ekt's - Virtual Infrastructure GReen meteR                      |
# | Copyright (C) 2010 National Documentation Center/NHRF ekt.vigr@gmail.com|
# |                                                                         |
# | This program is free software; you can redistribute it and/or           |
# | modify it under the terms of the GNU General Public License             |
# | as published by the Free Software Foundation; either version 2          |
# | of the License, or (at your option) any later version.                  |
# |                                                                         |
# | This program is distributed in the hope that it will be useful,         |
# | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
# | GNU General Public License for more details.                            |
# +-------------------------------------------------------------------------+
# | This code is designed, written, and maintained by the EKT/NHRF.         |
# +-------------------------------------------------------------------------+
# | http://code.google.com/p/e-vigr/                                        |
# +-------------------------------------------------------------------------+
#
#####################################################################
# set initial configuration parameters START
#####################################################################
# Set folder for storing temporary files/program state
set TMP "./EPOWER"
#set Virtual Infrastructure  hosts management  hostsnames, ILOMs hostnames/IPs, OIDs that return the actual consumpted power value
#OIDs are server dependent since from our own experience vendor tend to change power OIDs  even in minor ILOM revisions
#e.g. for SUN X4150
#set OID_VINFRA_WATT  .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.210; ILOM 2.0
#set OID_VINFRA_WATT .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196; ILOM 3.0
set VINFRA_HOST_CONF [list \
	{esx00.domain.tld 172.16.101.20 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196}\
	{esx01.domain.tld 172.16.101.21 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196}\
	{esx02.domain.tld 172.16.101.22 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196}\
	{esx03.domain.tld 172.16.101.23 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.199}\
	{esx04.domain.tld 172.16.101.24 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196}\
	{esx05.domain.tld 172.16.101.25 .1.3.6.1.4.1.42.2.70.101.1.1.8.1.4.196}	]
#set the base VM domains, we assume that VM identifiers are the VMs hostname + DOMAIN
set DOMAIN domain.tld
set DEBUG	1
#set the reference Virtual Server Idle power consumption
set Pidle	210
#set the reference Virtual Server maxisum power consumption
set Pmax 290
### Set the base VI server power consumption
set VINFRA_WATT 210000
### Set  cost in Euros per Kwh
# DEI for B2 gives 0.03915, energy.eu gives 0.0725 - 0.0864 <500kw for full cost of electricity in Greece
set EURO 0.0864
set CO2_per_KWh 1000
### Set administrative email for error messaging
set ERR_MAIL admin@domain.tld
#####################################################################
# set initial configuration parameters ENDS
#####################################################################
#
#####################################################################
##Set ESX specific parameters START
#####################################################################
#Set OID for returing VM ids from VI management as a list
set OID_VMs_IDs .1.3.6.1.4.1.6876.2.1.1.7
#####################################################################
##Initialize program variables
#####################################################################
set TOTAL_VINFRA_WATT 0
set Proj_Cons 0
#
###########Procedures Start
#
#######################################################################
#power_per_vm:Estimate projected power consumption per Virtual Machine#
#######################################################################
proc power_per_vm {vm_id vi_host } {
	global DEBUG
	global DOMAIN
	global Pidle
	global Pmax
	set status [exec snmpget -m ALL  -v2c -c public $vi_host .1.3.6.1.4.1.6876.2.1.1.6.$vm_id -O qv] 
	set ON [string match $status "poweredOn"]
	#check whether the VM is powered ON
	if $ON {
	# not used: read cpu number, memory size in order to project pidle,pmax. Eventually it does not has much significance
	#	 	set cpus [exec snmpget -m ALL  -v2c -c public $vi_host.domain.tld  .1.3.6.1.4.1.6876.2.1.1.9.$vm_id -O qv]
	#		set mem  [exec snmpget -m ALL  -v2c -c public $vi_host.domain.tld  .1.3.6.1.4.1.6876.2.1.1.5.$vm_id -O qv]
	#
		#requires that host name is the same with the VM identifier in the VMs management tool
		set host_name  [exec snmpget -m ALL  -v2c -c public $vi_host  .1.3.6.1.4.1.6876.2.1.1.2.$vm_id -O qv | cut -f 1]
	#		not used
	#		puts -nonewline "\t $host_name\t"
	#		get OS in order for the future to change MIB load values
	#		set os  [exec snmpget -m ALL  -v2c -c public $vi_host.domain.tld  .1.3.6.1.4.1.6876.2.1.1.4.$vm_id -O qv]
		#returns each core's processor load in % as a list
		if {[catch {exec snmpwalk -t 1 -v2c -c public $host_name.$DOMAIN hrProcessorLoad -O qv} Load]} {
			if $DEBUG	{
				# if processor load does not returns for any reason processor (e.g. custom not supported MIB,  other thatn hrProcessorLoad  OID, snmp agent disabled) load is set %1
				puts "minor ERROR: Host $host_name did not return SNMP hrProcessorLoad, setting LOAD=1%"
			}			
			set Load 1
		}
	#Calculate the average processor load from the cores load
		set Load_Proc [split $Load]
		set Total_Load 0
		foreach proc $Load_Proc	{
			if {[catch {expr $proc + $Total_Load} Total_Load]}	{	
				if $DEBUG	{
					puts "minor ERROR: $host_name returned malformed Core Load, average load calculation failed, setting POWER=P_idle"
				}
				return $Pidle;
			} 
		}
		set Total_Load [expr 1.0*$Total_Load/[llength $Load_Proc]]
		set Power [expr $Pidle + $Total_Load/100.0*($Pmax-$Pidle)]
		puts -nonewline "\t [string range "$host_name                       \t" 0 15]\t$Total_Load\t"
	} else {
		set Power 0
		#VM is powered off
	}

    	return $Power
}
#######################################################################
#Script Starts
#######################################################################
#walK through all the virtual infrastructure physical hosts
foreach host_inf $VINFRA_HOST_CONF   {

set VI_HOST [lindex $host_inf 0]
set ILOM_IP [lindex $host_inf 1]
set OID_VINFRA_WATT [lindex $host_inf 2]

	# return the actual consumed power by the VI host
	if {[catch  {set VINFRA_WATT [exec /usr/bin/snmpget -m / -v2c -c public $ILOM_IP $OID_VINFRA_WATT   -O qv]} err
  ]} {
		puts "ERROR $ILOM_IP $err"
		exec mail -s "Power Monitoring script:$err" $ERR_MAIL << $err;
	};
	puts "ILOM IP is $ILOM_IP, Consumption is $VINFRA_WATT (mW)"
	puts "\tVMHOSTNAME\t\tLOAD(%)\t\tVMID\t\tVI HOST\t\t\tVM PRJ. CONS. (W)\tCUM. PWR. CONS. (W)"
#	set VMs_VI_HOST($i) [exec snmpwalk -m / -v2c -c public  $VI_HOST($i).domain.tld .1.3.6.1.4.1.6876.2.1.1.6 | grep poweredOn | wc -l];
#	set SNMP_OUT($i) [exec snmpwalk -m ALL  -v2c -c public $VI_HOST($i).domain.tld  .1.3.6.1.4.1.6876.2.1.1.6 | grep poweredOn];
	#get the list of the VMs identifiers that run on this VI host
	if {[ catch {set VMs_IDs_String($VI_HOST) [exec snmpwalk -m ALL  -v2c -c public $VI_HOST $OID_VMs_IDs -O qv] } err
	]}	{
		puts "ERROR $VI_HOST $err"
		exec mail -s "Power Monitoring script:$err" $ERR_MAIL << $err
		continue;	
	}

	set VMs_list($VI_HOST) [split $VMs_IDs_String($VI_HOST)]
	#for each item on the list (vm) calculate the projected power consumption based on the VMs load
	foreach item  $VMs_list($VI_HOST) {
#		puts "\t VMID=$item on $VI_HOST"
                set vm_power [power_per_vm $item $VI_HOST];
                set Proj_Cons [expr $Proj_Cons +$vm_power];
#		puts "\t $vm_power (W) $Proj_Cons (W)"
		puts "\t$item\t\t$VI_HOST\t\t$vm_power\t\t$Proj_Cons"
        }
	set TOTAL_VINFRA_WATT [expr $TOTAL_VINFRA_WATT+$VINFRA_WATT];

#	set TOTAL_VMs [expr $TOTAL_VMs+$VMs_VI_HOST($i)]
#	puts "$VINFRA_WATT";
#	puts "VMs_VI_HOST(i) $VMs_VI_HOST($i)";
#	puts "VM_list $VMs_list($i)"  
}
set TOTAL_VINFRA_WATT [expr $TOTAL_VINFRA_WATT/1000]
set Power_Gained [expr $Proj_Cons - $TOTAL_VINFRA_WATT]
set Percentage_Gained [expr 100.0*$Power_Gained/$Proj_Cons]
#puts "Virtualization Platform Consumption $TOTAL_VINFRA_WATT W"
#puts "Total VMs: $TOTAL_VMs"
#puts "Projected power consumption: $Proj_Cons"
#puts "Total power gained: $Power_Gained"
#puts "Power reduction percent: $Percentage_Gained %"
set chan [open $TMP/kwh_gained.txt r]
set line [gets $chan]
set time_var [lindex $line 0]
set kwh_var [lindex $line 1]
close $chan
set cur_time [clock seconds]
set pass_time [expr $cur_time - $time_var]
set hours_pass [expr $pass_time/3600.0]
set kwh_new [expr $kwh_var + $Power_Gained*$hours_pass/1000.0]
#Calculate total power consumption based on three different PUEs 1.0,1.5,2.0,2.5
set total_kwh [list $kwh_new [expr $kwh_new*1.5] [expr $kwh_new*2.0] [expr $kwh_new*2.5]]
set total_co2 {}
set total_euro {}
foreach j $total_kwh	{
	puts "$j"	
	lappend total_co2 [expr $j/$CO2_per_KWh] 
	lappend total_euro [expr $j*$EURO] 
}
puts "Total kwh: $total_kwh \n Total CO2: $total_co2 \n Total E: $total_euro" 
puts "percentage:$Percentage_Gained watts_gained:$Power_Gained total_watt:$TOTAL_VINFRA_WATT power_cons:$Proj_Cons"
puts "Previous time is:$time_var Previous KWh:$kwh_var Current Time:$cur_time,Passed Time:$pass_time Hours Passed:$hours_pass New KWh saved:$kwh_new"

set chan [open $TMP/kwh_gained.txt w]
puts $chan "$cur_time $kwh_new"
close $chan


set chan [open $TMP/pwr_cacti.txt w]
puts -nonewline $chan "percentage:$Percentage_Gained watts_gained:$Power_Gained total_watt:$TOTAL_VINFRA_WATT power_cons:$Proj_Cons "
set i 0
foreach val $total_kwh	{
	puts -nonewline $chan "total_kwh_pue_$i:$val "
	incr i
}
set i 0
foreach val $total_co2  {
        puts -nonewline $chan "total_kwh_co2_$i:$val "
        incr i
}
set i 0
foreach val $total_euro  {
        puts -nonewline $chan "total_kwh_euro_$i:$val "
        incr i
}
close $chan

